﻿#include "PTWindowImpl.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ShellAPI.h>
#include <stdio.h>

void PTWWindowImpl::OutputEventCallback_Hook(void *pUserData, void(PTPTR *pOutputEventCallback)(void *pUserData, void *pOutputData))
{
	m_pOutputEventCallback = pOutputEventCallback;
	m_pOutputEventCallback_UserData = pUserData;

	//PTInvokeMain线程调用
	//一定发生在wWinMain线程调用Handle_Cache之后
	assert(m_hWindow_Cache != NULL);
	BOOL wbResult = ::PostMessageW(m_hWindow_Cache, WM_APP + 7, 0U, 0U);
	assert(wbResult != FALSE);
}

void PTWWindowImpl::InputEventCallback_Hook(void *pUserData, void(PTPTR *pInputEventCallback)(void *pUserData, void *pInputData))
{
	m_pInputEventCallback = pInputEventCallback;
	m_pInputEventCallback_UserData = pUserData;
}

void PTWWindowImpl::Parent_Set(PTWHWindow hWindowParent)
{
	if (hWindowParent == NULL)
	{
		LONG_PTR lResult = ::SetWindowLongPtrW(m_hWindow_Cache, GWL_STYLE, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE);
		assert(lResult != 0);

		HWND hDesktop = ::GetDesktopWindow();
		assert(hDesktop != NULL);

		HWND hParentPrevious = ::SetParent(m_hWindow_Cache, hDesktop);
		assert(hParentPrevious != NULL);
	}
	else
	{
		LONG_PTR lResult = ::SetWindowLongPtrW(m_hWindow_Cache, GWL_STYLE, WS_CHILD | WS_VISIBLE);
		assert(lResult != 0);

		HWND hParentPrevious = ::SetParent(m_hWindow_Cache, hWindowParent);
		assert(hParentPrevious != NULL);
	}
}

void PTWWindowImpl::Position_Set(uint32_t TopLeftX, uint32_t TopLeftY)
{
	BOOL wbResult = ::SetWindowPos(m_hWindow_Cache, NULL, TopLeftX, TopLeftY, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
	assert(wbResult != FALSE);
}

void PTWWindowImpl::Size_Set(uint32_t Width, uint32_t Height)
{
	BOOL wbResult = ::SetWindowPos(m_hWindow_Cache, NULL, 0, 0, Width, Height, SWP_NOZORDER | SWP_NOMOVE);
	assert(wbResult != FALSE);
}

void PTWWindowImpl::TermminateMessagePump()
{
	//PTInvokeMain线程调用
	//一定发生在wWinMain线程调用Handle_Cache之后
	assert(m_hWindow_Cache != NULL);

	DWORD ThreadId_wWinMain = ::GetWindowThreadProcessId(m_hWindow_Cache, NULL);
	BOOL wbResult = ::PostThreadMessageW(ThreadId_wWinMain, WM_QUIT, 0U, 0U);
	assert(wbResult != FALSE);
}

inline PTWWindowImpl::PTWWindowImpl()
{
	//OutputEventCallback
	m_pOutputEventCallback = NULL;
	m_pOutputEventCallback_UserData = NULL;
	//InputEventCallback
	m_pInputEventCallback = NULL;
	m_pInputEventCallback_UserData = NULL;
	//Handle Cache
	m_hWindow_Cache = NULL;
}

inline PTWWindowImpl::~PTWWindowImpl()
{

}

static unsigned __stdcall PTInvokeMain(void *pVoid);

//static LRESULT CALLBACK PTWndProc_Editor(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

//static LRESULT CALLBACK PTWndProc_XR(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

//static LRESULT CALLBACK PTWndProc_Desktop(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

static LRESULT CALLBACK PTWWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

#include "PTResource.h"

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int)
{
	PTWWindowImpl l_WindowImpl;

	ATOM hAtom;
	WNDCLASSEXW wc;
	wc.cbSize = sizeof(WNDCLASSEXW);
	wc.style = 0U;
	wc.lpfnWndProc = &PTWWndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = sizeof(void *);
	wc.hInstance = hInstance;
	wc.hIcon = ::LoadIconW(hInstance, MAKEINTRESOURCE(IDI_PTICON));
	wc.hCursor = ::LoadCursorW(NULL, IDC_ARROW);
	wc.hbrBackground = NULL;
	wc.lpszMenuName = NULL;
	WCHAR szClassName[33];
	::swprintf_s(szClassName, 33, L"PTWindowClass:0X%p", &wc);
	wc.lpszClassName = szClassName;
	wc.hIconSm = wc.hIcon;
	hAtom = ::RegisterClassExW(&wc);
	assert(hAtom != 0);

	HWND hDesktop = ::GetDesktopWindow();
	HMONITOR hMonitor = ::MonitorFromWindow(hDesktop, MONITOR_DEFAULTTONEAREST);
	MONITORINFOEXW MonitorInfo;
	MonitorInfo.cbSize = sizeof(MONITORINFOEXW);
	BOOL bRet = ::GetMonitorInfoW(hMonitor, &MonitorInfo);
	assert(bRet != 0);

	HWND hWnd = ::CreateWindowExW(WS_EX_APPWINDOW, MAKEINTATOM(hAtom), L"爱国者引擎", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, MonitorInfo.rcWork.left, MonitorInfo.rcWork.top, MonitorInfo.rcWork.right - MonitorInfo.rcWork.left, MonitorInfo.rcWork.bottom - MonitorInfo.rcWork.top, hDesktop, NULL, hInstance, static_cast<PTWWindowImpl *>(&l_WindowImpl));

	//HWND hWnd = ::CreateWindowExW(WS_EX_APPWINDOW, MAKEINTATOM(atom), L"PTWindow", WS_POPUP | WS_VISIBLE, MonitorInfo.rcWork.left, MonitorInfo.rcWork.top, MonitorInfo.rcWork.right, MonitorInfo.rcWork.bottom, hDesktop, NULL, hInstance, NULL);	

	assert(hWnd != NULL);

	l_WindowImpl.m_hWindow_Cache = hWnd;

	assert(l_WindowImpl.m_pOutputEventCallback == NULL);
#if 0
	if (m_pOutputEventCallback != NULL)
	{
		ABI::Windows::Foundation::Rect Bounds;
		HRESULT hResult = pWindow->get_Bounds(&Bounds);
		assert(SUCCEEDED(hResult));

		struct
		{
			uint32_t m_Type;
			PTWHDisplay m_hDisplay;
			PTWHWindow m_hWindow;
			uint32_t m_Width;
			uint32_t m_Height;
		}OutputEventData;
		OutputEventData.m_Type = PTWWindowImpl::PTOutputEventType_WindowCreated;
		OutputEventData.m_hDisplay = NULL;
		OutputEventData.m_hWindow = pWindow;
		OutputEventData.m_Width = static_cast<uint32_t>(Bounds.Width);
		OutputEventData.m_Height = static_cast<uint32_t>(Bounds.Height);

		m_pOutputEventCallback(m_pOutputEventCallback_UserData, &OutputEventData);
	}
#endif

	l_WindowImpl.m_pCmdLine_Cache = lpCmdLine;

	bool bResult;
	PTThread hThreadInvoke;
	bResult = ::PTThreadCreate(&PTInvokeMain, static_cast<PTWWindowImpl *>(&l_WindowImpl), &hThreadInvoke);
	assert(bResult);

	MSG msg;
	BOOL wbResult;
	while ((wbResult = ::GetMessageW(&msg, NULL, 0, 0)) != 0)
	{
		assert(wbResult != -1);

		::TranslateMessage(&msg);
		::DispatchMessageW(&msg);
	}
	assert(msg.message == WM_QUIT);

	//确保栈中的内存 PTInvokeParam ParamInvoke 在PTInvokeMain的整个生命期内是有效的
	bResult = ::PTThreadWait(&hThreadInvoke);
	assert(bResult);

	return static_cast<int>(msg.wParam);
}

static LRESULT CALLBACK PTWWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
	{
		CREATESTRUCT *pCreateStruct = reinterpret_cast<CREATESTRUCT *>(lParam);
		::SetWindowLongPtrW(hWnd, 0U, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
		return 0;
	}
	case WM_DESTROY:
	{
		PTWWindowImpl *pSingleton = reinterpret_cast<PTWWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->m_hWindow_Cache = NULL;

		::PostQuitMessage(0U);

		return 0;
	}
	case WM_SETFOCUS:
	{
		BOOL bRet;
		HMONITOR hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
		MONITORINFOEXW MonitorInfo;
		MonitorInfo.cbSize = sizeof(MONITORINFOEXW);
		bRet = ::GetMonitorInfoW(hMonitor, &MonitorInfo);
		assert(bRet != 0);
		bRet = ::SetWindowPos(hWnd, NULL, MonitorInfo.rcWork.left, MonitorInfo.rcWork.top, MonitorInfo.rcWork.right - MonitorInfo.rcWork.left, MonitorInfo.rcWork.bottom - MonitorInfo.rcWork.top, SWP_NOZORDER);
		assert(bRet != 0);
		return 0;
	}
	case WM_SIZE:
	{
		PTWWindowImpl *pSingleton = reinterpret_cast<PTWWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);

		if (pSingleton->m_pOutputEventCallback != NULL)
		{
			struct
			{
				uint32_t m_Type;
				uint32_t m_Width;
				uint32_t m_Height;
			}OutputEventData;
			OutputEventData.m_Type = PTWWindowImpl::PTOutputEventType_WindowResized;
			OutputEventData.m_Width = static_cast<uint32_t>(LOWORD(lParam));
			OutputEventData.m_Height = static_cast<uint32_t>(HIWORD(lParam));

			pSingleton->m_pOutputEventCallback(pSingleton->m_pOutputEventCallback_UserData, &OutputEventData);
		}

		return 0;
	}
	//窗口标题栏系统菜单关闭
	case WM_CLOSE:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	case WM_APP + 7:
	{
		PTWWindowImpl *pSingleton = reinterpret_cast<PTWWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));

		RECT rect;
		BOOL wbResult = ::GetClientRect(hWnd, &rect);
		assert(wbResult != FALSE);

		struct
		{
			uint32_t m_Type;
			PTWHDisplay m_hDisplay;
			PTWHWindow m_hWindow;
			uint32_t m_Width;
			uint32_t m_Height;
		}OutputEventData;
		OutputEventData.m_Type = PTWWindowImpl::PTOutputEventType_WindowCreated;
		OutputEventData.m_hDisplay = reinterpret_cast<HINSTANCE>(::GetWindowLongPtrW(hWnd, GWLP_HINSTANCE));//Win32中并不存在Display的概念，只是为了和Vulkan中一致
		OutputEventData.m_hWindow = hWnd;
		OutputEventData.m_Width = static_cast<uint32_t>(rect.right - rect.left);
		OutputEventData.m_Height = static_cast<uint32_t>(rect.bottom - rect.top);

		assert(pSingleton->m_pOutputEventCallback != NULL);
		pSingleton->m_pOutputEventCallback(pSingleton->m_pOutputEventCallback_UserData, &OutputEventData);
		return 0;
	}
	default:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	}
}

#if 0
static LRESULT CALLBACK PTWndProc_Editor(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
	{
		::CREATESTRUCT *pCreateStruct = reinterpret_cast<::CREATESTRUCT *>(lParam);
		::SetWindowLongPtrW(hWnd, 0U, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
		return 0;
	}
	//父窗口关闭
	case WM_DESTROY:
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->Handle_Cache(NULL);

		::PostQuitMessage(0U);

		return 0;
	}
	case WM_SIZE:
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->Size_Cache(static_cast<uint32_t>(LOWORD(lParam)), static_cast<uint32_t>(HIWORD(lParam)));
		return 0;
	}
	//未知情形？？？
	case WM_CLOSE:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	default:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	}
}

static LRESULT CALLBACK PTWndProc_XR(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
	{
		CREATESTRUCT *pCreateStruct = reinterpret_cast<CREATESTRUCT *>(lParam);
		::SetWindowLongPtrW(hWnd, 0U, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));

		//添加任务栏图标
		NOTIFYICONDATAW nid;
		nid.cbSize = sizeof(NOTIFYICONDATAW);
		nid.hWnd = hWnd;
		nid.uID = 7U;
		nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_INFO | NIF_SHOWTIP;
		nid.uCallbackMessage = (WM_APP + 7U);
		nid.hIcon = reinterpret_cast<HICON>(::GetClassLongPtrW(hWnd, GCLP_HICONSM));
		::wcscpy_s(nid.szTip, 64U, L"爱国者引擎（虚拟现实模式）");
		::wcscpy_s(nid.szInfo, 200U, L"右键双击退出");
		nid.uVersion = NOTIFYICON_VERSION_4;
		::wcscpy_s(nid.szInfoTitle, 48U, L"爱国者引擎（虚拟现实模式）运行中");
		nid.dwInfoFlags = NIIF_NONE;
		BOOL wbResult = ::Shell_NotifyIconW(NIM_ADD, &nid);
		assert(wbResult != 0);

		return 0;
	}
	case WM_DESTROY:
	{
		//删除任务栏图标
		NOTIFYICONDATAW nid;
		nid.cbSize = sizeof(NOTIFYICONDATAW);
		nid.hWnd = hWnd;
		nid.uID = 7U;
		BOOL wbResult = ::Shell_NotifyIconW(NIM_DELETE, &nid);
		assert(wbResult != 0);

		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->Handle_Cache(NULL);

		::PostQuitMessage(0U);

		return 0;
	}
	//窗口标题栏系统菜单关闭
	case WM_CLOSE:
	{
		assert(0);
		return 0;
	}
	//任务栏关闭
	case (WM_APP + 7U):
	{
		switch (LOWORD(lParam))//系统栏消息
		{
		case WM_RBUTTONDBLCLK://接收系统栏消息的窗口可以没有CS_DBLCLKS
		{
			::DestroyWindow(hWnd);
			return 0;
		}
		default:
		{
			return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
		}
		}
	}
	default:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	}
}

static LRESULT CALLBACK PTWndProc_Desktop(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
	{
		CREATESTRUCT *pCreateStruct = reinterpret_cast<CREATESTRUCT *>(lParam);
		::SetWindowLongPtrW(hWnd, 0U, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
		return 0;
	}
	case WM_DESTROY:
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->Handle_Cache(NULL);

		::PostQuitMessage(0U);

		return 0;
	}
	case WM_SETFOCUS:
	{
		BOOL bRet;
		HMONITOR hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
		MONITORINFOEXW MonitorInfo;
		MonitorInfo.cbSize = sizeof(MONITORINFOEXW);
		bRet = ::GetMonitorInfoW(hMonitor, &MonitorInfo);
		assert(bRet != 0);
		bRet = ::SetWindowPos(hWnd, NULL, MonitorInfo.rcWork.left, MonitorInfo.rcWork.top, MonitorInfo.rcWork.right - MonitorInfo.rcWork.left, MonitorInfo.rcWork.bottom - MonitorInfo.rcWork.top, SWP_NOZORDER);
		assert(bRet != 0);
		return 0;
	}
	case WM_SIZE:
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->Size_Cache(static_cast<uint32_t>(LOWORD(lParam)), static_cast<uint32_t>(HIWORD(lParam)));
		return 0;
	}
	//窗口标题栏系统菜单关闭
	case WM_CLOSE:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	default:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	}
}
#endif

#include "../../../Public/Application/PTAExport.h"
#include "../../../Public/System/Win32/PTSConvUTF.h"

static unsigned __stdcall PTInvokeMain(void *pVoid)
{
	PTWWindowImpl *pWindow = static_cast<PTWWindowImpl *>(pVoid);
	
	char CmdLineA[0X10000] = { "PTLauncher " };
	{
		size_t InCharsLeft = static_cast<size_t>(::wcslen(pWindow->m_pCmdLine_Cache)) + 1U;//包括'\0'
		size_t OutCharsLeft = 4096 - 11;
		::PTSConv_UTF16ToUTF8(pWindow->m_pCmdLine_Cache, &InCharsLeft, CmdLineA + 11, &OutCharsLeft);
	}

	char *argv[0X10000];
	int argc = 0;

	enum
	{
		WhiteSpace,
		Text
	}StateMachine = WhiteSpace;

	for (size_t i = 0U; CmdLineA[i] != '\0'; ++i)
	{
		switch (StateMachine)
		{
		case WhiteSpace:
		{
			if (!((CmdLineA[i] == ' ' || CmdLineA[i] == '\t' || CmdLineA[i] == '\r' || CmdLineA[i] == '\n')))
			{
				assert(argc < 0X10000);
				argv[argc] = CmdLineA + i;
				++argc;
				StateMachine = Text;
			}
		}
		case Text:
		{
			if ((CmdLineA[i] == ' ' || CmdLineA[i] == '\t' || CmdLineA[i] == '\r' || CmdLineA[i] == '\n'))
			{
				CmdLineA[i] = '\0';
				StateMachine = WhiteSpace;
			}
		}
		}

		assert(i < 0X10000U);
	}

	argv[argc] = NULL;

	int iResult = ::PTAMain(static_cast<IPTWWindow *>(pWindow), argc, argv);

	pWindow->TermminateMessagePump();

	return static_cast<unsigned>(iResult);
}